package metamodel.coreservices;

import metamodel.*;
import metamodel.visitor.ObjectGraphIterator;
import metamodel.visitor.ObjectGraphVisitorBaseImpl;

import javax.swing.*;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

/**
 * User: felix
 * Date: 11.06.2005
 */
public class TreeModelAdapter implements TreeModel{
    class CellRenderer extends DefaultTreeCellRenderer{
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
        {   Object mappedValue=value;
            Object meta = instance2meta.get(value);

            if (value instanceof NullObject){
                mappedValue = "Null";
            }
            if (meta instanceof CollectionRef) {
                CollectionRef collectionRef = (CollectionRef) meta;
                mappedValue=collectionRef.getName();
            }
            if (meta instanceof ObjectReference) {
                ObjectReference re= (ObjectReference) meta;
                mappedValue = re.getName();
            }
            return super.getTreeCellRendererComponent(tree, mappedValue, sel, expanded, leaf, row, hasFocus);    //To change body of overridden methods use File | Settings | File Templates.
        }
    }

    static class NullObject {

        public final static NullObject INSTANCE = new NullObject();
    }

    DefaultTreeCellRenderer renderer;
    ClassNode classNode;
    Object root;
    HashMap instance2meta;// this is somewhat dangerous as we assume there is only one record per javaclass

    public TreeModelAdapter(ClassNode classNode, Object root) {
        this.classNode = classNode;
        this.root = root;
        fillHash();
        renderer=new CellRenderer();
    }

    private void fillHash() {
        instance2meta =new HashMap(10);
        instance2meta.put(root, classNode);
        ObjectGraphIterator iter=new ObjectGraphIterator(classNode,new ObjectGraphVisitorBaseImpl() {
            public void visitCollection(CollectionRef property, Object value, Object parent) {
                instance2meta.put(value, property);
                }

            public void visitFlatProperty(FlatProperty property, Object value, Object parent, boolean hasNext) {
                instance2meta.put(value,property);
            }

            public void visitObject(ClassNode classNode, DeepProperty property, Object value, Object parent) {
                instance2meta.put(value,classNode);            }



        }, ObjectGraphIterator.TraversalMode.CHECK_TREE);

        iter.iterate(root);
    }


    public Object getRoot() {
        return root;
    }

    public int getChildCount(Object parent) {
        Object meta = instance2meta.get(parent);
        if (meta instanceof FlatProperty) {
            return 0;
        }
        if (meta instanceof CollectionRef) {
            CollectionRef cr = (CollectionRef) meta;
            return ((Collection)parent).size();
        }
        if (meta instanceof ClassNode) {
            ClassNode classNode = (ClassNode) meta;
            return classNode.getProperties().size();
        }
        if (meta instanceof ObjectReference) {
            ClassNode classNode = ((ObjectReference) meta).getElementType();
            return classNode.getProperties().size();
        }

        return 0;
    }

    public boolean isLeaf(Object node) {
        if (node instanceof NullObject)
            return true;
        Object meta = instance2meta.get(node);
        return meta instanceof FlatProperty;
    }

    public void addTreeModelListener(TreeModelListener l) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void removeTreeModelListener(TreeModelListener l) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public Object getChild(Object parent, int index) {
        Object meta = instance2meta.get(parent);
        if (meta instanceof CollectionRef) {
            CollectionRef cr = (CollectionRef) meta;
            Object colectionElement = ((List) parent).get(index);
            if (colectionElement==null) return NullObject.INSTANCE;
            return colectionElement;
        }
        if (meta instanceof ObjectReference) {
            ClassNode classNode = ((ObjectReference) meta).getElementType();
            Object objectReferenceValue = ((Property) classNode.getProperties().get(index)).getValue(parent);
            if (objectReferenceValue==null) return NullObject.INSTANCE;
            return objectReferenceValue;
        }
        if (meta instanceof ClassNode) {
            ClassNode classNode = ((ClassNode) meta);
            Object classNodeChild = ((Property) classNode.getProperties().get(index)).getValue(parent);
            if (classNodeChild==null) return NullObject.INSTANCE;;
            return classNodeChild;
        }
        System.out.println("Returned Null");
        return NullObject.INSTANCE;

    }

    public int getIndexOfChild(Object parent, Object child) {
        Object meta = instance2meta.get(parent);
        Object childmeta= instance2meta.get(child);
        if (meta instanceof CollectionRef) {
            CollectionRef cr = (CollectionRef) meta;
            return ((List)parent).indexOf(child);
        }
        if (meta instanceof ObjectReference) {
            ClassNode classNode = ((ObjectReference) meta).getElementType();
            return classNode.getProperties().indexOf(childmeta);
        }
        if (meta instanceof ClassNode) {
            ClassNode classNode = ((ClassNode) meta);
            return classNode.getProperties().indexOf(childmeta);
        }
        return 0;


    }

    public void valueForPathChanged(TreePath path, Object newValue) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public static void main(String[] args) {

    }

    public DefaultTreeCellRenderer getRenderer() {
        return renderer;
    }


}
